const serialport = require('serialport')
var port = null

CRC16 = data => {
    let c = 0xffff
    data.map(i => {
        let j = 8
        c ^= i
        while(j--) c = c >> 1 ^ (c & 1 ? 0xa001 : 0)
    })
    return [c & 0xff, c >> 8]
}

V = Vue.createApp({
created(){ listSerialPorts() },
data(){ return{
    iDevId: 1,
    iRegOrigin: 0,
    iRegLength: 100,
    iSendCount: 0,
    iRecieveCount: 0,
    iFrameInterval: 3000,

    bReading: false,
    bCRC: true,
    bConnect: false,
    bTimeout: true,
    bSending: false,
    bKeep: false,
    bHex: false,
    bToolModbus: false,
    bToolSerial: false,
    bToolSimulator: false,
    bToolChart: false,
    bToolDatapool: false,

    tFrameRequest: '',
    tFrameMonitor: '',

    xInterval: null,

    aFrameRecieve: [],

    config: {
        port: '',
        baudRate: 9600,
        dataBits: 8,
        parity: 'none',
        stopBits: 1,
    },
    option: {
        port: [],
        baudRate: [1200, 2400, 4800, 9600, 19200, 38400, 56000],
        dataBits: [7, 8],
        parity: ['none', 'odd', 'even'],
        stopBits: [1, 2]
    },
}},
methods:{
    connect(){
        if(V.bConnect)
            V.bConnect = false
        else if(V.config.port)               
            port = new serialport(V.config.port, V.config, err => {
                if(err == null){
                    V.bConnect = true
                    port.on('readable', () => {
                        if(V.bTimeout){
                            V.bTimeout = false
                            setTimeout(() => {
                                let t = port.read()
                                if(V.bReading){
                                    V.iRecieveCount++
                                    V.aFrameRecieve = t
                                }
                                V.bTimeout = true
                                V.tFrameMonitor += 'Rx: ' + V.myReduce(t)
                            }, 250) 
                        }                         
                    })
                }else console.log(err)
            })
    },
    sendFrame(){
        if(V.tFrameRequest)
            if(V.bSending){
                V.bSending = false
                clearInterval(V.xInterval)
            }else if(V.bConnect){
                let t = V.tFrameRequest.trim().split(/\s+/).map(i => parseInt(i, 16))
                if(V.bCRC) 
                    t = t.concat(CRC16(t))
                V.myWrite(t)
                if(V.bKeep){
                    V.bSending = true
                    V.xInterval = setInterval(V.myWrite, V.iFrameInterval, t) 
                }
            }        
    },
    resetCount(){
        V.iSendCount = 0
        V.iRecieveCount = 0
    },
    readFrame(){
        if(V.bReading){
            V.bReading = false
            clearInterval(V.xInterval)
        }else if(V.bConnect){
            V.myWrite(V.aFrameRead)
            V.xInterval = setInterval(V.myWrite, 500, V.aFrameRead)
            V.bReading = true
        }
    },
    myWrite(t){
        if(V.bConnect){
            port.write(t)
            V.tFrameMonitor += 'Tx: ' + V.myReduce(t) 
            if(V.bReading) V.iSendCount++
        }
    },
    myReduce(t){
        return t.reduce((a, b) =>
            a.toString(16).padStart(2, '0') + ' ' + 
            b.toString(16).padStart(2, '0')) + '\n'
    },
    myConcat(a, i, b){
        let m = a[i+3], n = a[i+4]
        if(m == undefined)
            return '0'
        else
            m <<= 8
        if(n == undefined)
            return '0'
        else
            n += m
        
        return b ? n.toString(16).padStart(4, '0').toUpperCase() : n
    }
},
computed:{
    aFrameRead(){
        let t = [V.iDevId, 0x03, 
                parseInt(V.iRegOrigin / 0xff), V.iRegOrigin % 0xff, 
                parseInt(V.iRegLength / 0xff), V.iRegLength % 0xff]
        return t.concat(CRC16(t))
    },
    aModBusOrder() { 
        return Array(this.iRegLength)
            .fill(this.iRegOrigin)
            .map((i, j) => 
                i + j + 40001 + '/0x' +
                (i+j).toString(16).padStart(2, '0').toUpperCase() + ': ' +
                this.myConcat(this.aFrameRecieve, j*2, this.bHex)
        )
    }
},
watch:{
    bConnect(b){
        if(!b){
            port.close()
            port.destroy()
            V.bReading = false
            V.bSending = false
            clearInterval(V.xInterval)  
        }
    },
    tFrameMonitor(){
        if(V.bToolSerial){
            let rec = document.getElementById('receive') 
            rec.scrollTop = rec.scrollHeight
        }
    }
}
}).mount('#app')

async function listSerialPorts(){
    await serialport.list().then((po, err) => {        
        if(err){
            console.log(err.message)
            return
        }
        V.option.port = po.filter(p => p.manufacturer != undefined).map(p => p.path).sort()

        if(V.option.port.length === 0)
            V.config.port = ''
        else if(V.option.port.indexOf(V.config.port) == -1){
            if(V.bConnect) V.bConnect = false
            V.config.port = V.option.port[0]
        }
    })
}
setInterval(listSerialPorts, 2000)